package cn.buddha.jesdp.strategy.complex;

import cn.buddha.jesdp.strategy.complex.enums.PaymentChannelEnum;
import cn.buddha.jesdp.strategy.complex.service.IPaymentOperate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName PaymentOperateFactory
 * @Author 友野浩二
 * @Version 1.0
 *
 * <pre>
 * 策略模式进阶版
 * 1- Spring容器会检测容器中的所有Bean，如果发现某个Bean实现了ApplicationContextAware接口,
 *    Spring容器会在创建该Bean之后，自动调用该Bean的setApplicationContextAware()方法.
 *
 *    当一个类实现了这个接口（ApplicationContextAware）之后，这个类就可以方便获得ApplicationContext中的所有bean。
 *    换句话说，就是这个类可以直接获取spring配置文件中，所有有引用到的bean对象。
 *
 * 2- 功能
 *      2-1 通过Spring机制将IPaymentOperate实现类加载到Map对象中;
 *      2-2 通过PaymentChannel枚举类型获取流程实现实例;
 *      2-3 通过支付渠道类别获取对应实现类的实例;
 * </pre>
 */
@Component
@Slf4j
public class PaymentOperateFactory implements ApplicationContextAware {

    /**
     * 存储接口 IPaymentOperate 的实现类
     */
    private static Map<PaymentChannelEnum, IPaymentOperate> payMap = new HashMap<>();

    /**
     * <pre>
     *  调用该方法时，会将容器本身作为参数传给该方法
     * </pre>
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("cn.buddha.jesdp.strategy.complex.PaymentOperateFactory started");
        Map<String, IPaymentOperate> map = applicationContext.getBeansOfType(IPaymentOperate.class);
        /*
         * <pre>
         * map内容:
         * 		{
         * 			payAli = cn.buddha.jesdp.strategy.complex.service.impl.PayAli,
         * 			payUnion = com.tong.service.impl.NsReceiptImpl@1050b5cc,
         * 			payWeChat = com.tong.service.impl.PoReceiptImpl@22cf6c72
         * 		}
         * </pre>
         */
        map.values().forEach(iPaymentOperate -> payMap.put(iPaymentOperate.getPaymentChannelClass(), iPaymentOperate));
    }

    /**
     * @Description // 根据不同的支付渠道获取IPaymentOperate接口的不同实现类
     **/
    public static IPaymentOperate getPayImpl(PaymentChannelEnum paymentChannelEnum) {
        return payMap.get(paymentChannelEnum);
    }
}
